﻿// (c) Copyright Fernando Cerqueira.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

// ReSharper disable CheckNamespace
namespace DevPhone.Controls
// ReSharper restore CheckNamespace
{
    [TemplateVisualState(Name = "Disabled", GroupName = "ViewStates")]
    [TemplateVisualState(Name = "Collapsed", GroupName = "ViewStates")]
    [TemplateVisualState(Name = "Expanded", GroupName = "ViewStates")]
    [TemplatePart(Name = "Content", Type = typeof(ContentControl))]
    [TemplatePart(Name = "ExpandCollapsArea", Type = typeof(Border))]
    public class ExpanderControl : ContentControl
    {
        #region event expander

        public event EventHandler<ExpandedChangeEventArgs> ChangeExpanded;

        public void OnChangeExpanded(ExpandedChangeEventArgs e)
        {
            var handler = ChangeExpanded;
            if (handler != null) handler(this, e);
        }

        #endregion

        #region private fields

        //flag loaded contrlol
        private bool _isloadedTemplate;
        private Grid _tapElementExpander;
        private ContentControl _contentElement;
        private Ellipse _ellipse;
        private Path _arrowExpander;
        private TextBlock _headerText;
        private static Brush _defaulControlBackground;
        private static Brush _defaultEllipseBrush;
        private static Brush _defaultEllipseArrow;
        private static Brush _defaultHeaderBackground;
        private static Brush _defaultHeaderBrush;
        private static Brush _defaultDisabled;
        private double _heightContent;


        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty ControlBackgroundProperty =
            DependencyProperty.Register("ControlBackground", typeof(Brush), typeof(ExpanderControl), new PropertyMetadata(null, ControlBackgroundChangedCallback));

        private static void ControlBackgroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (ExpanderControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.ControlBackground = _defaulControlBackground;
            }
            instance.Background = instance.ControlBackground;
            if (instance._isloadedTemplate)
            {
                instance._contentElement.Background = instance.ControlBackground;
            }
        }

        public Brush ControlBackground
        {
            get { return (Brush)GetValue(ControlBackgroundProperty); }
            set
            {
                SetValue(ControlBackgroundProperty, value);
                SetValue(BackgroundProperty, value);
            }
        }

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(string), typeof(ExpanderControl), new PropertyMetadata(null));

        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty IsExpandedProperty =
            DependencyProperty.Register("IsExpanded", typeof(bool), typeof(ExpanderControl), new PropertyMetadata(true, IsExpandedChangedCallback));

        private static void IsExpandedChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (ExpanderControl)dependencyObject;
            instance.ChangeVisualState(true);
        }

        public bool IsExpanded
        {
            get { return (bool)GetValue(IsExpandedProperty); }
            set
            {
                SetValue(IsExpandedProperty, value);
            }
        }

        public static readonly DependencyProperty EllipseBrushProperty =
            DependencyProperty.Register("EllipseBrush", typeof(Brush), typeof(ExpanderControl), new PropertyMetadata(null, EllipseBrushChangedCallback));

        private static void EllipseBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (ExpanderControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.EllipseBrush = _defaultEllipseBrush;
            }
            if (instance._isloadedTemplate && instance._ellipse != null)
            {
                instance._ellipse.Fill = instance.EllipseBrush;
            }
        }

        public Brush EllipseBrush
        {
            get { return (Brush)GetValue(EllipseBrushProperty); }
            set
            {
                SetValue(EllipseBrushProperty, value);
            }
        }

        public static readonly DependencyProperty DisabledBrushProperty =
            DependencyProperty.Register("DisabledBrush", typeof(Brush), typeof(ExpanderControl), new PropertyMetadata(null, DisabledBrushChangedCallback));

        private static void DisabledBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (ExpanderControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.DisabledBrush = _defaultDisabled;
            }
            if (instance._isloadedTemplate && !instance.IsEnabled)
            {
                instance._headerText.Foreground = instance.DisabledBrush;
                instance._ellipse.Fill = instance.DisabledBrush;
            }
        }

        public Brush DisabledBrush
        {
            get { return (Brush)GetValue(DisabledBrushProperty); }
            set
            {
                SetValue(DisabledBrushProperty, value);
            }
        }

        public static readonly DependencyProperty HeaderBackgroundProperty =
            DependencyProperty.Register("HeaderBackground", typeof(Brush), typeof(ExpanderControl), new PropertyMetadata(null, HeaderBackgroundChangedCallback));

        private static void HeaderBackgroundChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (ExpanderControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.HeaderBackground = _defaultHeaderBackground;
            }
            if (instance._isloadedTemplate)
            {
                instance._tapElementExpander.Background = instance.HeaderBackground;
            }
        }

        public Brush HeaderBackground
        {
            get { return (Brush)GetValue(HeaderBackgroundProperty); }
            set
            {
                SetValue(HeaderBackgroundProperty, value);
            }
        }

        public static readonly DependencyProperty HeaderBrushProperty =
            DependencyProperty.Register("HeaderBrush", typeof(Brush), typeof(ExpanderControl), new PropertyMetadata(null, HeaderBrushChangedCallback));

        private static void HeaderBrushChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (ExpanderControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.HeaderBrush = _defaultHeaderBrush;
            }
            if (instance._isloadedTemplate)
            {
                instance._headerText.Foreground = instance.HeaderBrush;
            }
        }

        public Brush HeaderBrush
        {
            get { return (Brush)GetValue(HeaderBrushProperty); }
            set
            {
                SetValue(HeaderBrushProperty, value);
            }
        }

        public static readonly DependencyProperty EllipseArrowProperty =
                DependencyProperty.Register("EllipseArrow", typeof(Brush), typeof(ExpanderControl), new PropertyMetadata(null, EllipseArrowChangedCallback));

        private static void EllipseArrowChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var instance = (ExpanderControl)dependencyObject;
            if (e.NewValue == null)
            {
                instance.EllipseArrow = _defaultEllipseArrow;
            }
            if (instance._isloadedTemplate && instance._arrowExpander != null)
            {
                instance._arrowExpander.Stroke = instance.EllipseArrow;
            }
        }

        public Brush EllipseArrow
        {
            get { return (Brush)GetValue(EllipseArrowProperty); }
            set
            {
                SetValue(EllipseArrowProperty, value);
            }
        }

        #endregion

        #region Load / template / VisualState / SizeChanged

        public ExpanderControl()
        {
            DefaultStyleKey = typeof(ExpanderControl);
            SizeChanged += ExpanderControlSizeChanged;
            IsEnabledChanged += ExpanderControlIsEnabledChanged;
        }

        private void ExpanderControlIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            VisualStateManager.GoToState(this, IsEnabled ? "Collapsed" : "Disabled", true);
            if (_headerText != null)
            {
                _headerText.Foreground = IsEnabled ? HeaderBrush : DisabledBrush;
                _ellipse.Fill = IsEnabled ? EllipseBrush : DisabledBrush;
            }
        }

        private void ExpanderControlSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (!double.IsNaN(Height))
            {
                Height = double.NaN;
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _ellipse = (Ellipse)GetTemplateChild("EllipseExpander");
            _arrowExpander = (Path)GetTemplateChild("ArrowExpander");

            _tapElementExpander = (Grid)GetTemplateChild("AreaHeader");
            _headerText = (TextBlock)GetTemplateChild("Header");
            _contentElement = (ContentControl)GetTemplateChild("Content");

            ClipBoundsControl.SetToBounds(_tapElementExpander, true);

            _defaultEllipseBrush = new SolidColorBrush((Color)Application.Current.Resources["PhoneAccentColor"]);
            _defaultEllipseArrow = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
            _defaultHeaderBackground = (Brush)Application.Current.Resources["PhoneTextBoxBrush"];
            _defaultHeaderBrush = (Brush)Application.Current.Resources["PhoneTextBoxForegroundBrush"];
            _defaultDisabled = (Brush)Application.Current.Resources["PhoneTextBoxReadOnlyBrush"];
            _defaulControlBackground = new SolidColorBrush(Color.FromArgb(01, 255, 255, 255));

            if (ControlBackground == null)
            {
                ControlBackground = _defaulControlBackground;
            }

            if (DisabledBrush == null)
            {
                DisabledBrush = _defaultDisabled;
            }

            if (EllipseArrow == null)
            {
                EllipseArrow = _defaultEllipseArrow;
            }
            if (EllipseBrush == null)
            {
                EllipseBrush = _defaultEllipseBrush;
            }
            if (HeaderBackground == null)
            {
                HeaderBackground = _defaultHeaderBackground;
            }
            if (HeaderBrush == null)
            {
                HeaderBrush = _defaultHeaderBrush;
            }

            if (_tapElementExpander != null)
            {
                _tapElementExpander.Tap += TapElementClick;
                _tapElementExpander.Background = HeaderBackground;
            }

            if (_headerText != null)
            {
                _headerText.Foreground = HeaderBrush;
            }
            if (_ellipse != null)
            {
                _ellipse.Fill = EllipseBrush;
            }
            if (_arrowExpander != null)
            {
                _arrowExpander.Stroke = EllipseArrow;
            }

            if (_contentElement != null)
            {
                _contentElement.Background = ControlBackground;
            }

            // Translate it into view once layout has been updated
            Dispatcher.BeginInvoke(() =>
            {
                _heightContent = _contentElement.ActualHeight;
                if (!IsEnabled)
                {
                    VisualStateManager.GoToState(this, "Disabled", true);
                }
                _headerText.Foreground = IsEnabled ? HeaderBrush : DisabledBrush;
                _ellipse.Fill = IsEnabled ? EllipseBrush : DisabledBrush;
                if (IsExpanded)
                {
                    _contentElement.Height = double.NaN;
                    _contentElement.Visibility = Visibility.Visible;
                    VisualStateManager.GoToState(this, "Expanded", true);
                }
                else
                {
                    _contentElement.Height = 0;
                    _contentElement.Visibility = Visibility.Collapsed;
                    VisualStateManager.GoToState(this, "Collapsed", true);
                }
                _isloadedTemplate = true;
            });

        }

        private void ChangeVisualState(bool useTransitions)
        {
            if (!_isloadedTemplate) return;
            
            if (IsExpanded)
            {
                if (_contentElement != null)
                {
                    var sb = CreateStoryboardExpandArea();
                    sb.Completed += SbExpandAreaCompleted;
                    _contentElement.Visibility = Visibility.Visible;
                    sb.Begin();

                }
                VisualStateManager.GoToState(this, "Expanded", useTransitions);
            }
            else
            {
                if (_contentElement != null)
                {
                    _heightContent = _contentElement.ActualHeight;
                    _contentElement.Height = _heightContent;
                    var sb = CreateStoryboardCollapsArea();
                    sb.Completed += SbCollapsAreaCompleted;
                    sb.Begin();
                }
                VisualStateManager.GoToState(this, "Collapsed", useTransitions);
            }
        }

        private void SbCollapsAreaCompleted(object sender, EventArgs e)
        {
            _contentElement.Visibility = Visibility.Collapsed;
        }

        private void SbExpandAreaCompleted(object sender, EventArgs e)
        {
            _contentElement.Height = double.NaN;
        }

        #endregion

        #region private methods

        private void TapElementClick(object sender, RoutedEventArgs e)
        {
            if (!IsEnabled) return;
            
            IsExpanded = !IsExpanded;
            OnChangeExpanded(new ExpandedChangeEventArgs(IsExpanded));
        }

        private Storyboard CreateStoryboardExpandArea()
        {
            var sb = new Storyboard();
            var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(300)), From = 0, To = _heightContent };
            Storyboard.SetTarget(da, _contentElement);
            Storyboard.SetTargetProperty(da, new PropertyPath("(FrameworkElement.Height)"));
            sb.Children.Add(da);
            return sb;
        }

        private Storyboard CreateStoryboardCollapsArea()
        {
            var sb = new Storyboard();
            var da = new DoubleAnimation { Duration = new Duration(TimeSpan.FromMilliseconds(300)), From = _heightContent, To = 0 };
            Storyboard.SetTarget(da, _contentElement);
            Storyboard.SetTargetProperty(da, new PropertyPath("(FrameworkElement.Height)"));
            sb.Children.Add(da);
            return sb;
        }

        #endregion

    }
}
